﻿using System;
using System.Collections.Generic;
using SpiceNet.Diagnostics;
using SpiceNet.Circuits;
using System.Numerics;
using SpiceNet.Parameters;
using SpiceNet.Simulations;

namespace SpiceNet.Models
{
    /// <summary>
    /// An instance of a voltage source
    /// </summary>
    public class VoltageSource : Instance
    {
        /// <summary>
        /// Get the parameters
        /// </summary>
        public override Dictionary<string, IP> ParamTable => _pTable;

        /// <summary>
        /// Private variables
        /// </summary>
        private Complex VSRCac;
        private int VSRCposNode, VSRCnegNode;

        /// <summary>
        /// Gets the ibr equation index
        /// </summary>
        public int VSRCbranch { get; private set; }

        #region Parameters
        private enum _c
        {
            VSRC_DC = 1,
            VSRC_AC = 2,
            VSRC_AC_MAG = 3,
            VSRC_AC_PHASE = 4,
            /* VSRC_PULSE = 5,
            VSRC_SINE = 6,
            VSRC_EXP = 7,
            VSRC_PWL = 8,
            VSRC_SFFM = 9, */
            VSRC_WAVEFORM = 5,
            VSRC_BR = 10,
            VSRC_FCN_TYPE = 11,
            VSRC_FCN_ORDER = 12,
            VSRC_FCN_COEFFS = 13,
            VSRC_AC_REAL = 14,
            VSRC_AC_IMAG = 15,
            VSRC_POS_NODE = 16,
            VSRC_NEG_NODE = 17,
            VSRC_CURRENT = 18,
            VSRC_POWER = 19,
            VSRC_D_F1 = 20,
            VSRC_D_F2 = 21
        }

        private static Dictionary<string, IP> _pTable = new Dictionary<string, IP>
        {
            { "dc", new IP(IF.IOPP, (int)_c.VSRC_DC, T.REAL, "D.C. source value") },
            { "acmag", new IP(IF.IOPPA, (int)_c.VSRC_AC_MAG, T.REAL, "A.C. Magnitude") },
            { "acphase", new IP(IF.IOPAAU, (int)_c.VSRC_AC_PHASE, T.REAL, "A.C. Phase") },
            /* { "pulse", new IP(IF.IP, (int)_c.VSRC_PULSE, T.REALVEC, "Pulse description") },
            { "sine", new IP(IF.IP, (int)_c.VSRC_SINE, T.REALVEC, "Sinusoidal source description") },
            { "sin", new IP(IF.IP, (int)_c.VSRC_SINE, T.REALVEC, "Sinusoidal source description") },
            { "exp", new IP(IF.IP, (int)_c.VSRC_EXP, T.REALVEC, "Exponential source description") },
            { "pwl", new IP(IF.IP, (int)_c.VSRC_PWL, T.REALVEC, "Piecewise linear description") },
            { "sffm", new IP(IF.IP, (int)_c.VSRC_SFFM, T.REALVEC, "Single freq. FM descripton") }, */
            { "waveform", new IP(IF.IOP, (int)_c.VSRC_WAVEFORM, T.WAVEFORM, "The waveform of the source") },
            { "pos_node", new IP(IF.OPU, (int)_c.VSRC_POS_NODE, T.INTEGER, "Positive node of source") },
            { "neg_node", new IP(IF.OPU, (int)_c.VSRC_NEG_NODE, T.INTEGER, "Negative node of source") },
            { "function", new IP(IF.OPU, (int)_c.VSRC_FCN_TYPE, T.INTEGER, "Function of the source") },
            { "order", new IP(IF.OPU, (int)_c.VSRC_FCN_ORDER, T.INTEGER, "Order of the source function") },
            { "coeffs", new IP(IF.OPU, (int)_c.VSRC_FCN_COEFFS, T.REALVEC, "Coefficients for the function") },
            { "acreal", new IP(IF.OPU, (int)_c.VSRC_AC_REAL, T.REAL, "AC real part") },
            { "acimag", new IP(IF.OPU, (int)_c.VSRC_AC_IMAG, T.REAL, "AC imaginary part") },
            { "ac", new IP(IF.IP, (int)_c.VSRC_AC, T.REALVEC, "AC magnitude, phase vector") },
            { "i", new IP(IF.OP, (int)_c.VSRC_CURRENT, T.REAL, "Voltage source current") },
            { "p", new IP(IF.OP, (int)_c.VSRC_POWER, T.REAL, "Instantaneous power") },
            { "distof1", new IP(IF.IP, (int)_c.VSRC_D_F1, T.REALVEC, "f1 input for distortion") },
            { "distof2", new IP(IF.IP, (int)_c.VSRC_D_F2, T.REALVEC, "f2 input for distortion") }
        };

        /// <summary>
        /// The waveform for transient simulations
        /// </summary>
        public Parameter<Waveform> VSRCWaveform { get; } = new Parameter<Waveform>();
        public Parameter<double> VSRCdcValue { get; } = new Parameter<double>();
        public Parameter<double> VSRCacMag { get; } = new Parameter<double>();
        public Parameter<double> VSRCacPhase { get; } = new Parameter<double>();
        // public Parameter<double> VSRCfuncT { get; } = new Parameter<double>();
        // public Parameter<double> VSRCcoeffs { get; } = new Parameter<double>();
        public Parameter<double> VSRCdF1mag { get; } = new Parameter<double>();
        public Parameter<double> VSRCdF2mag { get; } = new Parameter<double>();
        public Parameter<double> VSRCdF1phase { get; } = new Parameter<double>();
        public Parameter<double> VSRCdF2phase { get; } = new Parameter<double>();
        // public double VSRCacReal { get; } = new Parameter<double>();
        // public double VSRCacImag { get; } = new Parameter<double>();
        #endregion

        /// <summary>
        /// Constructor
        /// </summary>
        public VoltageSource(string name) : base(name, 2, typeof(VoltageSourceModel)) { }

        /// <summary>
        /// Set parameter
        /// </summary>
        protected override void Param(int id, object value, Circuit ckt = null)
        {
            double[] v;
            switch ((_c)id)
            {
                case _c.VSRC_DC:
                    VSRCdcValue.Par((double)value);
                    break;
                case _c.VSRC_AC_MAG:
                    VSRCacMag.Par((double)value);
                    break;
                case _c.VSRC_AC_PHASE:
                    VSRCacPhase.Par((double)value);
                    break;
                case _c.VSRC_AC:
                    v = (double[])value;
                    switch (v.Length)
                    {
                        case 2:
                            VSRCacPhase.Par(v[1]);
                            goto case 1;
                        case 1:
                            VSRCacMag.Par(v[0]);
                            break;
                        case 0:
                            VSRCacMag.Par(VSRCacMag.Value);
                            break;
                        default:
                            throw new BadParameterException();
                    }
                    break;
                case _c.VSRC_WAVEFORM:
                    VSRCWaveform.Par((Waveform)value);
                    break;
                case _c.VSRC_D_F1:
                    v = (double[])value;
                    switch (v.Length)
                    {
                        case 2:
                            VSRCdF1phase.Par(v[1]);
                            VSRCdF1mag.Par(v[0]);
                            break;
                        case 1:
                            VSRCdF1mag.Par(v[0]);
                            VSRCdF1phase.Par(0.0);
                            break;
                        case 0:
                            VSRCdF1mag.Par(1.0);
                            VSRCdF1phase.Par(0.0);
                            break;
                        default:
                            throw new BadParameterException();
                    }
                    break;
                case _c.VSRC_D_F2:
                    v = (double[])value;
                    switch (v.Length)
                    {
                        case 2:
                            VSRCdF2phase.Par(v[1]);
                            VSRCdF2mag.Par(v[0]);
                            break;
                        case 1:
                            VSRCdF2mag.Par(v[0]);
                            VSRCdF2phase.Par(0.0);
                            break;
                        case 0:
                            VSRCdF2mag.Par(1.0);
                            VSRCdF2phase.Par(0.0);
                            break;
                        default:
                            throw new BadParameterException();
                    }
                    break;
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Ask parameter
        /// </summary>
        protected override object Ask(int id, Circuit ckt = null)
        {
            switch ((_c)id)
            {
                case _c.VSRC_DC:
                    return VSRCdcValue.Value;
                case _c.VSRC_AC_MAG:
                    return VSRCacMag.Value;
                case _c.VSRC_AC_PHASE:
                    return VSRCacPhase.Value;
                case _c.VSRC_WAVEFORM:
                    return VSRCWaveform.Value;
                case _c.VSRC_AC:
                    return new double[] { VSRCacMag, VSRCacPhase };
                case _c.VSRC_NEG_NODE:
                    return VSRCnegNode;
                case _c.VSRC_POS_NODE:
                    return VSRCposNode;
                case _c.VSRC_AC_REAL:
                    return VSRCac.Real;
                case _c.VSRC_AC_IMAG:
                    return VSRCac.Imaginary;
                case _c.VSRC_CURRENT:
                    if (ckt.CurrentAnalysis.HasFlag(AnalysisType.DoingAc))
                        throw new AcCurrentPowerException();
                    return ckt.State.Solution[VSRCbranch];
                case _c.VSRC_POWER:
                    if (ckt.CurrentAnalysis.HasFlag(AnalysisType.DoingAc))
                        throw new AcCurrentPowerException();
                    return (ckt.State.Solution[VSRCposNode] - ckt.State.Solution[VSRCnegNode]) * -ckt.State.Solution[VSRCbranch];
                default:
                    throw new BadParameterException();
            }
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name">The name of the voltage source</param>
        /// <param name="posnode">The name of the positive node</param>
        /// <param name="negnode">The name of the negative node</param>
        /// <param name="dc">The DC value of the voltage</param>
        public VoltageSource(string name, string posnode, string negnode, double dc) : this(name)
        {
            Connect(posnode, negnode);
            VSRCdcValue.Par(dc);
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="name">The name of the voltage source</param>
        /// <param name="posnode">The name of the positive node</param>
        /// <param name="negnode">The name of the negative node</param>
        /// <param name="w">The waveform of the voltage in time</param>
        public VoltageSource(string name, string posnode, string negnode, Waveform w) : this(name)
        {
            Connect(posnode, negnode);
            VSRCWaveform.Par(w);
        }

        /// <summary>
        /// Setup the voltage source
        /// </summary>
        /// <param name="model"></param>
        /// <param name="ckt"></param>
        public override void Setup(Circuit ckt, Model model, object p = null)
        {
            // Bind nodes
            var nodes = BindNodes(ckt, NodeType.Current);
            VSRCposNode = nodes[0].Number;
            VSRCnegNode = nodes[1].Number;
            VSRCbranch = nodes[2].Number;

            // Setup the waveform if given
            if (VSRCWaveform.Value != null)
                VSRCWaveform.Value.Setup(this, ckt);
        }

        /// <summary>
        /// Do temperature dependent
        /// </summary>
        /// <param name="model"></param>
        /// <param name="ckt"></param>
        public override void Temperature(Circuit ckt, Model model, object p = null)
        {
            // [EDIT] Do not touch ac, acM or acP
            // These are handled in the Parameter classes in a more logical way
            /* if(here->VSRCacGiven && !here->VSRCacMGiven) {
                here->VSRCacMag = 1;
            }
            if(here->VSRCacGiven && !here->VSRCacPGiven) {
                here->VSRCacPhase = 0;
            } */

            // Calculate the voltage complex value
            if (!VSRCdcValue.Given)
            {
                /* no DC value - either have a transient value, or none */
                if (VSRCWaveform.Given)
                    CircuitWarning.Warning(this, string.Format("{0}: no DC value, transient time 0 value used", Name));
                else
                    CircuitWarning.Warning(this, string.Format("{0}: has no value, DC 0 assumed", Name));
            }
            double radians = VSRCacPhase * Circuit.CONSTPI / 180.0;
            VSRCac = new Complex(VSRCacMag * Math.Cos(radians), VSRCacMag * Math.Sin(radians));
        }

        /// <summary>
        /// Load the voltage source
        /// </summary>
        /// <param name="model"></param>
        /// <param name="ckt"></param>
        public override void Load(Circuit ckt, Model model, object p = null)
        {
            CircuitState state = ckt.State;
            double time = 0.0;

            state.Matrix[VSRCposNode, VSRCbranch] += 1.0;
            state.Matrix[VSRCbranch, VSRCposNode] += 1.0;
            state.Matrix[VSRCnegNode, VSRCbranch] -= 1.0;
            state.Matrix[VSRCbranch, VSRCnegNode] -= 1.0;

            if ((ckt.Mode & (Circuit.Modes.DcOp | Circuit.Modes.DcTranCurve)) != 0 && VSRCdcValue.Given)
            {
                /* grab DC value */
                state.Rhs[VSRCbranch] += ckt.SrcFact * VSRCdcValue;
            }
            else
            {
                if ((ckt.Mode & Circuit.Modes.Dc) != 0)
                    time = 0.0;
                else
                    time = state.Time;

                // Use the transient functions [EDIT] We do it with the waveform class
                if (VSRCWaveform.Given && (VSRCWaveform.Value != null))
                    state.Rhs[VSRCbranch] += VSRCWaveform.Value.At(time); // Takes care of the whole switch
                else
                    state.Rhs[VSRCbranch] += VSRCdcValue; // No function specified: use the DC value
            }

            // loadDone:
        }

        /// <summary>
        /// Load the voltage source for AC analysis
        /// </summary>
        /// <param name="model"></param>
        /// <param name="ckt"></param>
        public override void AcLoad(Circuit ckt, Model model, object p = null)
        {
            CircuitStateComplex state = ckt.StateComplex;
            state.Matrix[VSRCposNode, VSRCbranch] += 1.0;
            state.Matrix[VSRCbranch, VSRCposNode] += 1.0;
            state.Matrix[VSRCnegNode, VSRCbranch] -= 1.0;
            state.Matrix[VSRCbranch, VSRCnegNode] -= 1.0;
            state.Rhs[VSRCbranch] += VSRCac;
        }

        /// <summary>
        /// Accept while adding breakpoints if necessary
        /// </summary>
        /// <param name="model"></param>
        /// <param name="ckt"></param>
        public override void Accept(Circuit ckt, Model model, object p = null)
        {
            if (VSRCWaveform.Given)
                VSRCWaveform.Value?.Accept(ckt);
        }
    }
}
